package com.gitee.wsl.data.store.datastore

import androidx.datastore.core.DataMigration
import androidx.datastore.core.DataStore
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.preferences.core.*
import com.gitee.wsl.Lib
import com.gitee.wsl.data.store.AbstractStoreServer
import com.gitee.wsl.data.store.StoreServer
import com.gitee.wsl.ioDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import kotlin.reflect.KClass

class DataStoreServer(private val dataStore: DataStore<Preferences>): AbstractStoreServer() {

    override fun <T : Any> getOrNull(key: String, type: KClass<T>): T?= runBlocking {
        //if(type == null) throw IllegalArgumentException("This type can be get from Preferences")
        return@runBlocking when (type) {
            Int::class -> {
                dataStore.data.map { setting ->
                    setting[intPreferencesKey(key)] ?: type
                }.first() as T
            }
            Long::class -> {
                dataStore.data.map { setting ->
                    setting[longPreferencesKey(key)] ?: type
                }.first() as T
            }
            Double::class -> {
                dataStore.data.map { setting ->
                    setting[doublePreferencesKey(key)] ?: type
                }.first() as T
            }
            Float::class -> {
                dataStore.data.map { setting ->
                    setting[floatPreferencesKey(key)] ?: type
                }.first() as T
            }
            Boolean::class -> {
                dataStore.data.map { setting ->
                    setting[booleanPreferencesKey(key)]?: type
                }.first() as T
            }
            String::class -> {
                dataStore.data.map { setting ->
                    setting[stringPreferencesKey(key)] ?: type
                }.first() as T
            }
            else -> {
//                val stringKey = stringPreferencesKey(name)
//                val json =  dataStore.data.map { preferences ->
//                    preferences[stringKey]
//                }.first()
//                json?.decodeJsonFromString<T>()

                throw IllegalArgumentException("This type can be get into DataStore")
            }
        }
    }

    override fun <T : Any> realPut(key: String, value: T):Unit = runBlocking {
        dataStore.edit { setting ->
            when (value) {
                is Int -> setting[intPreferencesKey(key)] = value
                is Long -> setting[longPreferencesKey(key)] = value
                is Double -> setting[doublePreferencesKey(key)] = value
                is Float -> setting[floatPreferencesKey(key)] = value
                is Boolean -> setting[booleanPreferencesKey(key)] = value
                is String -> setting[stringPreferencesKey(key)] = value
                else -> throw IllegalArgumentException("This type can be saved into DataStore")
            }
        }
    }


    override fun realRemove(key: String): Unit = runBlocking {
        dataStore.edit { setting ->
            setting.remove(preferencesKey<String>(key))
        }
    }

    override fun isExits(key: String): Boolean = runBlocking{
         dataStore.data.map { it.contains(preferencesKey<String>(key)) }.firstOrNull() == true
    }

    /**
     * An inline function to get a key for an [T] preference.
     *
     * @param name the name of the preference
     * @return the Preferences.Key<T> for [name]
     */
    @Suppress("UNCHECKED_CAST")
    private inline fun <reified T : Any> preferencesKey(name: String): Preferences.Key<T> {
        return when (T::class) {
            Int::class -> intPreferencesKey(name) as Preferences.Key<T>
            String::class -> stringPreferencesKey(name) as Preferences.Key<T>
            Boolean::class -> booleanPreferencesKey(name) as Preferences.Key<T>
            Float::class -> floatPreferencesKey(name) as Preferences.Key<T>
            Long::class -> longPreferencesKey(name) as Preferences.Key<T>
            Set::class -> stringSetPreferencesKey(name) as Preferences.Key<T>
            else -> throw IllegalArgumentException("Type not supported: ${T::class}")
        }
    }
}

fun StoreServer.Companion.dataStore(
    path: String,
    corruptionHandler: ReplaceFileCorruptionHandler<Preferences>? = null,
    coroutineScope: CoroutineScope = CoroutineScope(Lib.ioDispatcher + SupervisorJob()),
    migrations: List<DataMigration<Preferences>> = emptyList(),
): StoreServer = DataStoreServer(
    createDataStoreWithDefaults(
        corruptionHandler = corruptionHandler,
        coroutineScope = coroutineScope,
        migrations = migrations,
        pathGetter = { path }
    )
)